Spring AOP和AspectJ学习实践

Spring AOP和AspectJ学习实践

使用Spring AOP

Spring AOP需要的依赖:

1
2
3
4
5
<dependency>
<groupId>${spring.group}</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring.version}</version>
</dependency>

Spring中定义切面的方法,就是在配置文件中声明pointcut,以及pointcut对应的advice(有before,after等),如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<aop:config>
<aop:aspect ref="audience"><!--定义一个audience切面-->

<aop:before pointcut=
"execution(* com.vonzhou.springinaction.springidolaop.Performer.perform(..))"
method="takeSeats" />


<aop:before pointcut=
"execution(* com.vonzhou.springinaction.springidolaop.Performer.perform(..))"
method="turnOffCellPhones" />


<aop:after-returning pointcut=
"execution(* com.vonzhou.springinaction.springidolaop.Performer.perform(..))"
method="applaud" /> <!--返回后通知-->


<aop:after-throwing pointcut=
"execution(* com.vonzhou.springinaction.springidolaop.Performer.perform(..))"
method="demandRefund" />


</aop:aspect>
</aop:config>

Pointcut定义的是切点,pointcut表明针对哪些方法需要AOP,然后基于AOP可以定义切点之前,之后,返回值,剖出异常时调用的方法,其实就是代理模式和装饰模式。

完整代码示例

Spring + aspectj

AspectJ比Spring AOP更加强大,是运行时织入(weave)。需要的依赖有:

1
2
3
4
5
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.9</version>
</dependency>

使用aspectj例子如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
@Aspect
public class Audience {
@Pointcut(
"execution(* com.vonzhou.springinaction.springidol.Performer.perform(..))")
public void performance() {
}

@Before("performance()")
public void takeSeats() {
System.out.println("The audience is taking their seats.");
}

@Before("performance()")
public void turnOffCellPhones() {
System.out.println("The audience is turning off their cellphones");
}

@AfterReturning("performance()")
public void applaud() {
System.out.println("CLAP CLAP CLAP CLAP CLAP");
}

@AfterThrowing("performance()")
public void demandRefund() {
System.out.println("Boo! We want our money back!");
}

@Around("performance()")
public void around(ProceedingJoinPoint proceedingJoinPoint){
System.out.println("before around ====");
try {
proceedingJoinPoint.proceed();
System.out.println("after around ===");
} catch (Throwable throwable) {
throwable.printStackTrace();
}

}
}

可以看到Around annotation注解使用的场景是在 Before annotation执行之前(进入方法之前)执行,然后 ProceedingPonintcut.proceed()执行完之后做一些统计处理,方法返回,然后执行After annotation。

完整代码示例

对Spring MVC Controller进行AOP

如何对Spring MVC Controller进行AOP呢?见网上说:对于Spring MVC Controller实行AOP不能一般处理,因为Controller中的方法映射处理,其实都交给了AnnotationMethodHandlerAdapter.handle(),所以要针对其定义pointcut。然而并没有用!(亲测, 如下)而且AnnotationMethodHandlerAdapter现在Deprecatedas of Spring 3.2, in favor of RequestMappingHandlerAdapter

这样不行:

1
2
3
@Around("execution(* org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.handle(..))")
public Object aa(ProceedingJoinPoint pjp) throws Throwable {
}

所以如果有这样的场景的话,可以使用Spring的HandlerInterceptor,参见

遇到的问题

1
Caused by: org.xml.sax.SAXParseException; lineNumber: 42; columnNumber: 29; cvc-complex-type.2.4.c: ?????????, ??????? 'aop:aspectj-autoproxy' ????

原因在xml配置文件中没有写完整,少了http://www.springframework.org/schema/aop/spring-aop.xsd

参考资源

Spring AOP vs AspectJ
AspectJ Runtime